#!/usr/bin/env python
#
# options_setup.py
#
# Version : 0.7
# Date : 20080520
#
# Author : Bernard Czenkusz
# Email  : bernie@skipole.co.uk

# options_setup.py - Displays options setup panel and is a module in sm_gui
# package, run as part of the skipolemonitor gui interface
#
# Copyright (c) 2007 Bernard Czenkusz
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#

""" This is a module in the sm_gui package, and defines the Options setup panel

It is run as part of the skipolemonitor gui interface.
"""

import wx
import os
import time
import makehtml
import validate
import webserver
from net import *


class OptionsSetup(wx.Panel):
    """The OptionsSetup Panel which is used by one of the tabs, in the SetupNotebook Window"""
    #
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        #
        # Create the options sizer, splitting the Panel vertically
        self.options_sizer=wx.BoxSizer(wx.VERTICAL)
        #
        # Create the input fields and text in a grid sizer
        self.fields_sizer=wx.FlexGridSizer(rows=0, cols=2, hgap=20, vgap=20)
        button_size = (100,40)
        #
        #
        # Enable web server
        self.web_server_enable_field = wx.CheckBox(self, -1, "")
        self.fields_sizer.Add(self.web_server_enable_field, 0, wx.ALIGN_RIGHT)
        if net_cfg["WEB_SERVER_ENABLED"]=="0":
            self.web_server_enable_field.SetValue(0)
        else:
            self.web_server_enable_field.SetValue(1)
        web_server_enable_text=wx.StaticText(self, -1, "Enable the built-in Web Server")
        self.fields_sizer.Add(web_server_enable_text, 0, wx.ALIGN_LEFT)
        #
        # Web server port
        self.web_server_port_field = wx.TextCtrl(self, -1, net_cfg["PORT"], size=(60,-1))
        self.web_server_port_field.SetToolTipString("The internal web server listens on this port")
        self.fields_sizer.Add(self.web_server_port_field, 0, wx.ALIGN_RIGHT)
        web_server_port_text=wx.StaticText(self, -1, "The web server TCP port (default 8000)")
        self.fields_sizer.Add(web_server_port_text, 0, wx.ALIGN_LEFT)
        #
        # Ping logs
        self.ping_logs_field = wx.TextCtrl(self, -1, net_cfg["LOG_RECORDS"], size=(60,-1))
        self.ping_logs_field.SetToolTipString("The number of results to display on each host web page.")
        self.fields_sizer.Add(self.ping_logs_field, 0, wx.ALIGN_RIGHT)
        ping_logs_text=wx.StaticText(self, -1, "The number of ping results to display\n(default 50, maximum 500)")
        self.fields_sizer.Add(ping_logs_text, 0, wx.ALIGN_LEFT)
        #
        # Make column 1 growable - this allows the right descriptions to be large
        self.fields_sizer.AddGrowableCol(1,0)
        #
        # Add grid fields_sizer to the main sizer
        self.options_sizer.Add(self.fields_sizer, 0, wx.TOP | wx.ALIGN_CENTER, 40)
        #
        # Create the html folder option
        html_folder_text=wx.StaticText(self, -1, "Set the folder where the html files will be created:")
        self.options_sizer.Add(html_folder_text, 0, wx.TOP | wx.ALIGN_CENTER, 20)
        #
        # Create a horizontal folder_sizer to hold the html field, and browser button
        self.folder_sizer=wx.BoxSizer(wx.HORIZONTAL)
        #
        self.html_folder_field = wx.TextCtrl(self, -1, net_cfg["HTTPROOT"], size=(400,-1))
        self.html_folder_field.SetToolTipString("The folder which will hold html files")
        self.folder_sizer.Add(self.html_folder_field, 0, wx.ALIGN_RIGHT)
        #
        self.html_folder_button = wx.Button(self, label="Browse..")
        #self.cancel_button.Enable(False)
        self.html_folder_button.SetToolTipString("Click to browse for a folder")
        self.folder_sizer.Add(self.html_folder_button, 0, wx.ALIGN_LEFT)
        #
        # Add folder_sizer to the main sizer
        self.options_sizer.Add(self.folder_sizer, 0, wx.ALL | wx.ALIGN_CENTER, 10)
        #
        # Create the polling frequency radiobox
        ping_frequency_text=wx.StaticText(self, -1, "Set the interval at which hosts are polled:")
        self.options_sizer.Add(ping_frequency_text, 0, wx.TOP | wx.ALIGN_CENTER, 20)
        #
        sampleList = ['Ping hosts every minute',
                      'Every two minutes',
                      'Every five minutes',
                      'Every ten minutes']
        self.ping_frequency_field = wx.RadioBox(self, -1, "Set:",
                                                wx.DefaultPosition, wx.DefaultSize,
                                                sampleList, 1, wx.RA_SPECIFY_COLS
                                                )
        if net_cfg["POLL_INTERVAL"]=="60":
            self.ping_frequency_field.SetSelection(0)
        elif net_cfg["POLL_INTERVAL"]=="120":
            self.ping_frequency_field.SetSelection(1)
        elif net_cfg["POLL_INTERVAL"]=="300":
            self.ping_frequency_field.SetSelection(2)
        else:
            self.ping_frequency_field.SetSelection(3)
        #
        self.ping_frequency_field.SetItemToolTip(0,"To avoid creating excessive traffic; choose a longer polling interval if you have a large number of hosts.")
        self.ping_frequency_field.SetItemToolTip(1,"To avoid creating excessive traffic; choose a longer polling interval if you have a large number of hosts.")
        self.ping_frequency_field.SetItemToolTip(2,"Five minutes is the default, and suitable for most cases.")
        self.ping_frequency_field.SetItemToolTip(3,"Ten minutes between polls may be suitable for very large networks.")
        #
        self.options_sizer.Add(self.ping_frequency_field, 0, wx.ALL | wx.ALIGN_CENTER, 10)
        #
        # -- Apply - Cancel -- buttons
        #
        # Create a horizontal button_sizer to hold the buttons
        self.button_sizer=wx.BoxSizer(wx.HORIZONTAL)
        #
        # Apply button
        self.apply_button = wx.Button(self, wx.ID_APPLY, size=button_size)
        self.apply_button.Enable(False)
        self.apply_button.SetToolTipString("Click to apply changes.")
        self.button_sizer.Add(self.apply_button, 0, wx.ALL | wx.ALIGN_CENTER, 10)
        #
        # Cancel button
        self.cancel_button = wx.Button(self, wx.ID_CANCEL, size=button_size)
        self.cancel_button.Enable(False)
        self.cancel_button.SetToolTipString("Click to cancel changes.")
        self.button_sizer.Add(self.cancel_button, 0, wx.ALL | wx.ALIGN_CENTER, 10)
        #
        # Add button_sizer to the main sizer
        self.options_sizer.Add(self.button_sizer, 0, wx.TOP | wx.ALIGN_CENTER, 40)
        #
        # Set the size to the panel
        self.SetSizer(self.options_sizer)
        #
        # Bind the fields
        self.Bind(wx.EVT_CHECKBOX, self.web_server_enable, self.web_server_enable_field)
        self.Bind(wx.EVT_TEXT, self.web_server_port, self.web_server_port_field)
        self.Bind(wx.EVT_TEXT, self.ping_logs, self.ping_logs_field)
        self.Bind(wx.EVT_TEXT, self.html_folder, self.html_folder_field)
        self.Bind(wx.EVT_RADIOBOX, self.ping_frequency, self.ping_frequency_field)
        #
        # Bind the html folder browse button
        self.Bind(wx.EVT_BUTTON, self.html_folder_browse, self.html_folder_button)
        #
        # Bind the Apply and Cancel buttons
        self.Bind(wx.EVT_BUTTON, self.apply_button_pressed, self.apply_button)
        self.Bind(wx.EVT_BUTTON, self.cancel_button_pressed, self.cancel_button)
        

    # Options field binding methods, - enable Apply and Cancel buttons,

    def web_server_enable(self, event):
        """Web Server Enable checkbox toggled"""
        self.apply_button.Enable(True)
        self.cancel_button.Enable(True)
        return

    def web_server_port(self, event):
        """Limit web server port to five characters"""
        self.apply_button.Enable(True)
        self.cancel_button.Enable(True)
        text = event.GetString()
        if len(text)>5:
            pos=self.web_server_port_field.GetInsertionPoint()
            self.web_server_port_field.Remove(5,len(text)+1)
            self.web_server_port_field.SetInsertionPoint(pos)

    def ping_logs(self, event):
        """Limit number of ping logs to three characters"""
        self.apply_button.Enable(True)
        self.cancel_button.Enable(True)
        text = event.GetString()
        if len(text)>3:
            pos=self.ping_logs_field.GetInsertionPoint()
            self.ping_logs_field.Remove(3,len(text)+1)
            self.ping_logs_field.SetInsertionPoint(pos)

    def html_folder(self, event):
        """Limit html folder path to 100 characters"""
        self.apply_button.Enable(True)
        self.cancel_button.Enable(True)
        text = event.GetString()
        if len(text)>100:
            pos=self.html_folder_field.GetInsertionPoint()
            self.html_folder_field.Remove(100,len(text)+1)
            self.html_folder_field.SetInsertionPoint(pos)

    def html_folder_browse(self, event):
        """Browse for a html folder"""
        self.apply_button.Enable(True)
        self.cancel_button.Enable(True)
        dlg = wx.DirDialog(self, "Choose a folder:",
                          style=wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST)
        #
        # If the user selects OK, then we process the dialog's data.
        # This is done by getting the path data from the dialog - BEFORE
        # we destroy it. 
        if dlg.ShowModal() == wx.ID_OK:
            # Set the html_folder_field
            self.html_folder_field.SetValue(dlg.GetPath())
        #
        dlg.Destroy()

    def ping_frequency(self, event):
        """Ping frequency radio box toggled"""
        self.apply_button.Enable(True)
        self.cancel_button.Enable(True)
        return

    # ------------- Apply button pressed

    def apply_button_pressed(self, event):
        """ Apply button - check and apply all fields"""
        self.apply_button.Enable(False)
        #
        # set the net reload flag, to stop polling
        set_net_reload()
        #
        # Changing the html directory is potentially damaging, so check this first, and
        # give the user a chance to cancel
        #
        old_httproot_value = net_cfg["HTTPROOT"]
        new_httproot_value = str(self.html_folder_field.GetValue()).strip()
        #
        # change_folder is a flag to indicate if the folder should be changed or not
        #
        change_folder = True
        if old_httproot_value == new_httproot_value:
            # There's no change, so just continue
            change_folder = False
        #
        # Does the new folder exist?
        if change_folder and (not os.path.exists(new_httproot_value)):
            wx.MessageBox("The new HTML folder must exist, reverting to original folder.",
                               caption="ERROR",
                               style=wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP)
            self.html_folder_field.SetValue(old_httproot_value)
            change_folder = False
        #
        # Is it a directory?
        if change_folder and (not os.path.isdir(new_httproot_value)):
            wx.MessageBox("The new HTML folder must exist, reverting to original folder.",
                               caption="ERROR",
                               style=wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP)
            self.html_folder_field.SetValue(old_httproot_value)
            change_folder = False
        #
        # Do I have write permission?
        if change_folder and (not os.access(new_httproot_value, os.W_OK)):
            wx.MessageBox("Cannot write to the new HTML folder, reverting to original folder.",
                               caption="ERROR",
                               style=wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP)
            self.html_folder_field.SetValue(old_httproot_value)
            change_folder = False
        #
        # Check the user definetly wants to change
        if change_folder:
            #
            # 'Are you sure' dialogue
            retCode=wx.MessageBox("Any existing contents of this new html folder will be deleted and overwritten.\nAre you sure!",
                              "Change HTML folder",
                              wx.OK | wx.CANCEL | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP)
            if retCode != wx.OK :
                self.html_folder_field.SetValue(old_httproot_value)
                change_folder = False
        #
        if change_folder:
            # So yes, the user is sure, now do the change
            net_cfg["HTTPROOT"] = new_httproot_value
            if not makehtml.new_httproot():
                # Recieved an error, revert back!
                self.html_folder_field.SetValue(old_httproot_value)
                net_cfg["HTTPROOT"] = old_httproot_value
                wx.MessageBox("Could not write to the new HTML folder, reverting to original folder.",
                               caption="ERROR",
                               style=wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP)
            else:
                # So new folder is set
                # Create an index.html network loading page
                makehtml.network_loading()
        #
        # Enable Web Server
        old_web_value = net_cfg["WEB_SERVER_ENABLED"]
        if self.web_server_enable_field.GetValue():
            net_cfg["WEB_SERVER_ENABLED"]="1"
        else:
            net_cfg["WEB_SERVER_ENABLED"]="0"
        #
        # Change of web server port
        old_port_value = net_cfg["PORT"]
        text=self.web_server_port_field.GetValue()
        text=str(text).strip()
        #
        # For Linux, restrict to the higher ports to stop the temptation
        # to run as superuser
        if os.name == "posix":
            if validate.is_text_integer(text, 1024, 65535):
                net_cfg["PORT"]=text
            else:
                wx.MessageBox("The TCP web server port must be between 1024 and 65535",
                               caption="ERROR",
                               style=wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP)
                self.web_server_port_field.SetValue(net_cfg["PORT"])
        else:
            # Remove this restriction for other OS
            if validate.is_text_integer(text, 0, 65535):
                net_cfg["PORT"]=text
            else:
                wx.MessageBox("The TCP web server port must be between 0 and 65535",
                               caption="ERROR",
                               style=wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP)
                self.web_server_port_field.SetValue(net_cfg["PORT"])
        #
        # Has the port changed? A web service restart may be necessary
        if old_port_value != net_cfg["PORT"]:
            # Is the web server running?
            if old_web_value == "1" and net_cfg["WEB_SERVER_ENABLED"]=="1":
                # A restart is necessary, shut the web server
                net_cfg["WEB_SERVER_ENABLED"]="0"
                time.sleep(2)
                # It should have shut down, so restart it
                old_web_value = "0"
                net_cfg["WEB_SERVER_ENABLED"]="1"
        #
        # If the web server is to be disabled, this will automatically happen
        # as the web server is constantly checking the WEB_SERVER_ENABLED flag
        # However if the web server has been enabled from a previous disabled
        # position, then the server must be started.
        if old_web_value == "0" and net_cfg["WEB_SERVER_ENABLED"]=="1":
            if not webserver.startup(net_cfg["PORT"]):
                wx.MessageBox("The web server is enabled but unable to start, a program restart may be necessary.",
                           caption="ERROR",
                           style=wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP)
        #
        # Ping logs
        text=self.ping_logs_field.GetValue()
        text=str(text).strip()
        if validate.is_text_integer(text, 1, 500):
            net_cfg["LOG_RECORDS"]=text
        else:
            wx.MessageBox("The ping results displayed must be between 1 and 500",
                           caption="ERROR",
                           style=wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP)
            self.ping_logs_field.SetValue(net_cfg["LOG_RECORDS"])
        #
        # Ping frequency
        if self.ping_frequency_field.GetSelection() == 0:
            net_cfg["POLL_INTERVAL"]="60"
        if self.ping_frequency_field.GetSelection() == 1:
            net_cfg["POLL_INTERVAL"]="120"
        if self.ping_frequency_field.GetSelection() == 2:
            net_cfg["POLL_INTERVAL"]="300"
        if self.ping_frequency_field.GetSelection() == 3:
            net_cfg["POLL_INTERVAL"]="600"
        #
        # Save config file to disc
        if not writeconfigtofile():
            wx.MessageBox("ERROR: Unable to save changes to disk",
                           caption="ERROR",
                           style=wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP)
        #
        # Unset the net reload flag, so polling can continue
        unset_net_reload()
        #
        # As Apply has been pressed, disable the Apply and Cancel buttons
        self.apply_button.Enable(False)
        self.cancel_button.Enable(False)
        

    # ------------- Cancel button pressed


    def cancel_button_pressed(self, event):
        """ Cancel button - return option fields to original values"""
        #
        if net_cfg["WEB_SERVER_ENABLED"]=="0":
            self.web_server_enable_field.SetValue(0)
        else:
            self.web_server_enable_field.SetValue(1)
        #
        # Web Server TCP port
        self.web_server_port_field.SetValue(net_cfg["PORT"])
        #
        # Number of ping logs to display
        self.ping_logs_field.SetValue(net_cfg["LOG_RECORDS"])
        #
        # HTML folder
        self.html_folder_field.SetValue(net_cfg["HTTPROOT"])
        #
        # Ping frequency
        if net_cfg["POLL_INTERVAL"]=="60":
            self.ping_frequency_field.SetSelection(0)
        elif net_cfg["POLL_INTERVAL"]=="120":
            self.ping_frequency_field.SetSelection(1)
        elif net_cfg["POLL_INTERVAL"]=="300":
            self.ping_frequency_field.SetSelection(2)
        else:
            self.ping_frequency_field.SetSelection(3)
        #
        self.apply_button.Enable(False)
        self.cancel_button.Enable(False)



